/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.ans.jpodcast.responsehandler;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * Basic implementation of a response handler with various helper methods for subclasses to call.
 * @author andy
 */
public class GenericResponseHandler implements ResponseHandler {

    private Document document;
    private XPathFactory xpathFactory;

    /**
     * Builds the XML document object from the PCP response and assigns it to a local variable.
     * Other response handlers extend this class, and call the superclass method in their handleResponse()
     * method, then do whatever custom handling they need to do.
     * @param response HttpResponse object from the HttpClient library
     * @return the response object again
     * @throws ClientProtocolException
     * @throws IOException
     */
    public Object handleResponse(HttpResponse response) throws ClientProtocolException, IOException {

        try {

            // Make sure that we got a 200 response code (HTTP OK)
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                throw new IOException("PCP server did not return status code 200 - we got " + statusCode + " instead!");
            }

            // Grab the XML document from the response
            setDocument(getXMLDocument(response));

            // Set up the XPathFactory for querying this object
            xpathFactory = XPathFactory.newInstance();


            // Make sure the <status> element in the response has the text value "success"
            // This element is present in every PCP response
            XPath xpath = xpathFactory.newXPath();
            try {
                String statusText = (String)xpath.evaluate("//status", document, XPathConstants.STRING);
                if (!statusText.equals("success")) {
                    throw new IOException("PCP server says the request failed - status text was " + statusText);
                }
            } catch(XPathExpressionException e) {
                // Rethrow as a masked exception for the client application to deal with
                throw new IOException(e);
            }

            // We need to return something useful, so you can have the response object back
            return response;

        } catch (ParserConfigurationException e) {
            throw new IOException(e);
        } catch (SAXException e) {
            throw new IOException(e);
        }
    }

    private Document getXMLDocument(HttpResponse response) throws
            ParserConfigurationException,
            IOException,
            SAXException {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        InputStream is = response.getEntity().getContent();
        Document doc = db.parse(is);
        return doc;

    }

    /**
     * Gets a simple <key> <string> style property from the PCP plist response. Returns "" if
     * the expression is invalid and causes an exception.
     * @param keyName The value of the <key></key> element
     * @return The value of the <string></string>
     */
    protected String getSimpleProperty(String keyName) {
        String xpathExpression = "//string[preceding-sibling::key[1]='" + keyName + "']";
        XPath xpath = xpathFactory.newXPath();
        try {
            String result = (String) xpath.evaluate(xpathExpression, document, XPathConstants.STRING);
            return result;
        } catch (XPathExpressionException e) {
            return "";
        }
    }

    protected ArrayList<String> getArrayProperty(String keyName) {
        String xpathExpression = "//array[preceding-sibling::key[1]='" + keyName + "']";
        XPath xpath = xpathFactory.newXPath();
        try {
            ArrayList<String> valueArray = new ArrayList<String>();

            Node node = (Node) xpath.evaluate(xpathExpression, document, XPathConstants.NODE);
            NodeList nodeList = node.getChildNodes();

            int listLength = nodeList.getLength();
            for (int i = 0; i < listLength; i++) {
                Node currentNode = nodeList.item(i);
                valueArray.add(currentNode.getTextContent());
            }

            return valueArray;

        } catch (XPathExpressionException e) {
            return null;
        }
    }

    /**
     * @return the document
     */
    public Document getDocument() {
        return document;
    }

    /**
     * @param document the document to set
     */
    public void setDocument(Document document) {
        this.document = document;
    }
}
